package org.mybatis.generator.config.xml;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.JDBCConnectionConfiguration;
import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
import org.mybatis.generator.config.JavaTypeResolverConfiguration;
import org.mybatis.generator.config.ModelType;
import org.mybatis.generator.config.PluginConfiguration;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.mybatis.generator.config.TableConfiguration;

public class ContextConfiguration {
	private String id;
	private String targetRuntime;
	private String defaultModelType;
	private String introspectedColumnImpl;

	private Context context;

	//	private Properties attributes;// <property> (0..N)
	private List<PluginConfiguration> pluginConfigurations = new ArrayList<PluginConfiguration>();// <plugin> or <ibatorPlugin> (0..N)
	private CommentGeneratorConfiguration commentGeneratorConfiguration;// <commentGenerator> (0 or 1)
	private JDBCConnectionConfiguration jdbcConnectionConfiguration;// <jdbcConnection> (1 Required)
	private JavaTypeResolverConfiguration javaTypeResolverConfiguration; // <javaTypeResolver> (0 or 1)
	private JavaModelGeneratorConfiguration javaModelGeneratorConfiguration; // <javaModelGenerator> (1 Required)
	private SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration; // <sqlMapGenerator> (0 or 1)
	private JavaClientGeneratorConfiguration javaClientGeneratorConfiguration; // <javaClientGenerator> or <daoGenerator> (0 or 1)
	private List<TableConfiguration> tableConfigurations = new ArrayList<TableConfiguration>();// <table> (1..N)

	public ContextConfiguration() {
		this.context = new Context(null);
	}

	/**
	 * 生成 context
	 */
	public XmlElement genContext() {
		initContext();
		XmlElement context = this.context.toXmlElement();

		for (int i = 0, length = pluginConfigurations.size(); i < length; i++) {
			context.addElement(pluginConfigurations.get(i).toXmlElement());
		}
		if (commentGeneratorConfiguration != null) {
			context.addElement(commentGeneratorConfiguration.toXmlElement());
		}
		if (jdbcConnectionConfiguration != null) {
			context.addElement(jdbcConnectionConfiguration.toXmlElement());
		} else {
			throw new RuntimeException("jdbcConnectionConfiguration is required");
		}
		if (javaTypeResolverConfiguration != null) {
			context.addElement(javaTypeResolverConfiguration.toXmlElement());
		}
		if (javaModelGeneratorConfiguration != null) {
			context.addElement(javaModelGeneratorConfiguration.toXmlElement());
		} else {
			throw new RuntimeException("javaModelGeneratorConfiguration is required");
		}
		if (sqlMapGeneratorConfiguration != null) {
			context.addElement(sqlMapGeneratorConfiguration.toXmlElement());
		}
		if (javaClientGeneratorConfiguration != null) {
			context.addElement(javaClientGeneratorConfiguration.toXmlElement());
		}
		if (tableConfigurations.size() < 1) {
			throw new RuntimeException("tableConfiguration at least one");
		} else {
			for (int i = 0, length = tableConfigurations.size(); i < length; i++) {
				context.addElement(tableConfigurations.get(i).toXmlElement());
			}
		}
		return context;
	}

	// -------------------------------------------------------
	// ------------- PluginConfiguration   ------------------
	// -------------------------------------------------------
	public void addPluginConfiguration(String configurationType) {
		addPluginConfiguration(configurationType, null);
	}

	public void addPluginConfiguration(String configurationType, Properties attributes) {
		PluginConfiguration pluginConfiguration = GeneratorUtils.newInstance(PluginConfiguration.class, configurationType,
				attributes);
		addPluginConfiguration(pluginConfiguration);
	}

	public void addPluginConfiguration(PluginConfiguration pluginConfiguration) {
		this.pluginConfigurations.add(pluginConfiguration);
	}

	// -------------------------------------------------------
	// ----------  CommentGeneratorConfiguration   -----------
	// -------------------------------------------------------

	public void setCommentGeneratorConfiguration(Properties attributes) {
		setCommentGeneratorConfiguration(null, attributes);
	}

	public void setCommentGeneratorConfiguration(String configurationType, Properties attributes) {
		CommentGeneratorConfiguration commentGeneratorConfiguration = GeneratorUtils.newInstance(
				CommentGeneratorConfiguration.class, configurationType, attributes);
		setCommentGeneratorConfiguration(commentGeneratorConfiguration);
	}

	public void setCommentGeneratorConfiguration(CommentGeneratorConfiguration commentGeneratorConfiguration) {
		this.commentGeneratorConfiguration = commentGeneratorConfiguration;
	}

	// -------------------------------------------------------
	// -----------  JdbcConnectionConfiguration   -----------
	// -------------------------------------------------------
	public void setJdbcConnectionConfiguration(String driverClass, String connectionURL, String userId, String password) {
		JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();
		jdbcConnectionConfiguration.setDriverClass(driverClass);
		jdbcConnectionConfiguration.setConnectionURL(connectionURL);
		jdbcConnectionConfiguration.setUserId(userId);
		jdbcConnectionConfiguration.setPassword(password);
		setJdbcConnectionConfiguration(jdbcConnectionConfiguration);
	}

	public void setJdbcConnectionConfiguration(JDBCConnectionConfiguration jdbcConnectionConfiguration) {
		this.jdbcConnectionConfiguration = jdbcConnectionConfiguration;
	}

	// -------------------------------------------------------
	// -----------  JdbcConnectionConfiguration   -----------
	// -------------------------------------------------------

	public void setJavaTypeResolverConfiguration(Properties attributes) {
		setCommentGeneratorConfiguration(null, attributes);
	}

	public void setJavaTypeResolverConfiguration(String configurationType, Properties attributes) {
		JavaTypeResolverConfiguration javaTypeResolverConfiguration = GeneratorUtils.newInstance(
				JavaTypeResolverConfiguration.class, configurationType, attributes);
		setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);
	}

	public void setJavaTypeResolverConfiguration(JavaTypeResolverConfiguration javaTypeResolverConfiguration) {
		this.javaTypeResolverConfiguration = javaTypeResolverConfiguration;
	}

	// -------------------------------------------------------
	// -----------  JavaModelGeneratorConfiguration   ---------
	// -------------------------------------------------------
	public void setJavaModelGeneratorConfiguration(String targetPackage, String targetProject, Properties attributes) {
		JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = GeneratorUtils.newInstance(
				JavaModelGeneratorConfiguration.class, attributes);
		javaModelGeneratorConfiguration.setTargetPackage(targetPackage);
		javaModelGeneratorConfiguration.setTargetProject(targetProject);
		setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);
	}

	public void setJavaModelGeneratorConfiguration(JavaModelGeneratorConfiguration javaModelGeneratorConfiguration) {
		this.javaModelGeneratorConfiguration = javaModelGeneratorConfiguration;
	}

	// -------------------------------------------------------
	// -----------  SqlMapGeneratorConfiguration   ---------
	// -------------------------------------------------------
	public void setSqlMapGeneratorConfiguration(String targetPackage, String targetProject, Properties attributes) {
		SqlMapGeneratorConfiguration javaModelGeneratorConfiguration = GeneratorUtils.newInstance(
				SqlMapGeneratorConfiguration.class, attributes);
		sqlMapGeneratorConfiguration.setTargetPackage(targetPackage);
		sqlMapGeneratorConfiguration.setTargetProject(targetProject);
		setSqlMapGeneratorConfiguration(javaModelGeneratorConfiguration);
	}

	public void setSqlMapGeneratorConfiguration(SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration) {
		this.sqlMapGeneratorConfiguration = sqlMapGeneratorConfiguration;
	}

	// -------------------------------------------------------
	// -----------  JavaClientGeneratorConfiguration   --------
	// -------------------------------------------------------
	public void setJavaClientGeneratorConfiguration(String configurationType, String targetPackage, String targetProject,
			Properties attributes) {
		JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = GeneratorUtils.newInstance(
				JavaClientGeneratorConfiguration.class, configurationType, attributes);
		javaClientGeneratorConfiguration.setTargetPackage(targetPackage);
		javaClientGeneratorConfiguration.setTargetProject(targetProject);
		setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);
	}

	public void setJavaClientGeneratorConfiguration(JavaClientGeneratorConfiguration javaClientGeneratorConfiguration) {
		this.javaClientGeneratorConfiguration = javaClientGeneratorConfiguration;
	}

	// -------------------------------------------------------
	// -----------  JavaClientGeneratorConfiguration   --------
	// -------------------------------------------------------
	public void addTableConfiguration(TableConfiguration tableConfiguration) {
		this.tableConfigurations.add(tableConfiguration);
	}

	public void addTableConfiguration(String tableName, String domainObjectName) {
		TableConfiguration tableConfiguration = new TableConfiguration(context);
		tableConfiguration.setTableName(tableName);
		tableConfiguration.setDomainObjectName(domainObjectName);

		tableConfiguration.setCountByExampleStatementEnabled(false);
		tableConfiguration.setUpdateByExampleStatementEnabled(false);
		tableConfiguration.setDeleteByExampleStatementEnabled(false);
		tableConfiguration.setSelectByExampleStatementEnabled(false);
		tableConfiguration.setSelectByExampleQueryId("false");
		addTableConfiguration(tableConfiguration);
	}

	public void setId(String id) {
		this.id = id;
	}

	public void setTargetRuntime(String targetRuntime) {
		this.targetRuntime = targetRuntime;
	}

	public void setDefaultModelType(String defaultModelType) {
		this.defaultModelType = defaultModelType;
	}

	public void setIntrospectedColumnImpl(String introspectedColumnImpl) {
		this.introspectedColumnImpl = introspectedColumnImpl;
	}

	private void initContext() {
		ModelType mt = defaultModelType == null ? null : ModelType.getModelType(defaultModelType);

		setDefaultModelType(mt);
		context.setId(id);
		if (stringHasValue(introspectedColumnImpl)) {
			context.setIntrospectedColumnImpl(introspectedColumnImpl);
		}
		if (stringHasValue(targetRuntime)) {
			context.setTargetRuntime(targetRuntime);
		}
	}

	private void setDefaultModelType(ModelType defaultModelType) {
		try {
			Field f = Context.class.getDeclaredField("defaultModelType");
			f.setAccessible(true);
			f.set(context, defaultModelType);
		} catch (Exception e) {
			throw new RuntimeException("defaultModelType 赋值错误.", e);
		}
	}

}
